<!DOCTYPE html>
<html lang="en">
<!-- https://codepen.io/rachsmith/pen/wgoNad -->
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    html,
    body {
      width: 100%;
    }

    body {
      font-size: 40px;
      font-family: 'Bungee Shade', cursive;
      color: #222;
      background: #222;
    }

    .box {
      position: relative;
      overflow: hidden;
      cursor: pointer;
      float: left;
    }

    .box:hover .border {
      -webkit-transform: scale(0.94);
      transform: scale(0.94);
      -webkit-transition-duration: 140ms;
      transition-duration: 140ms;
    }

    .box:hover .text {
      opacity: 1;
      -webkit-transform: translate3d(0, 0, 0);
      transform: translate3d(0, 0, 0);
      -webkit-transition-duration: 140ms;
      transition-duration: 140ms;
    }

    .box:hover .image-wrap {
      -webkit-transform: scale(1);
      transform: scale(1);
      opacity: 1;
      -webkit-transition-duration: 140ms;
      transition-duration: 140ms;
    }

    .border,
    .text,
    .image {
      width: 100%;
      height: 100%;
      position: absolute;
      pointer-events: none;
    }

    .image-wrap {
      position: absolute;
      width: 130%;
      height: 130%;
      left: -15%;
      top: -15%;
      -webkit-transform: scale(0.8);
      transform: scale(0.8);
      -webkit-transition: 280ms ease-out;
      transition: 280ms ease-out;
      pointer-events: none;
      opacity: 0.74;
    }

    .border {
      left: -30px;
      top: -30px;
      border: 30px solid #222;
      box-sizing: content-box;
      -webkit-transition: 360ms ease-in-out;
      transition: 360ms ease-in-out;
    }

    .text {
      display: -webkit-box;
      display: flex;
      -webkit-box-align: center;
      align-items: center;
      -webkit-box-pack: center;
      justify-content: center;
      opacity: 0;
      -webkit-transform: translate3d(0, -4%, 0);
      transform: translate3d(0, -4%, 0);
      -webkit-transition: 280ms ease-out;
      transition: 280ms ease-out;
      text-align: center;
    }

    .image {
      background-size: cover;
    }

    /* box sizing */
    .box {
      width: 100%;
      height: 100vw;
      float: left;
    }

    @media (min-width: 500px) {
      .box {
        width: 50%;
        height: 50vw;
      }
    }

    @media (min-width: 800px) {
      .box {
        width: 33.333333%;
        height: 33.33333vw;
      }
    }

    @media (min-width: 1200px) {
      .box {
        width: 25%;
        height: 25vw;
      }
    }

    /* image backgrounds */
    .image-0 {
      background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/53148/box-image_0.jpg);
    }

    .image-1 {
      background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/53148/box-image_1.jpg);
    }

    .image-2 {
      background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/53148/box-image_2.jpg);
    }

    .image-3 {
      background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/53148/box-image_3.jpg);
    }

    .image-4 {
      background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/53148/box-image_4.jpg);
    }

    .image-5 {
      background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/53148/box-image_5.jpg);
    }

    .image-6 {
      background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/53148/box-image_6.jpg);
    }

    .image-7 {
      background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/53148/box-image_7.jpg);
    }

    .image-8 {
      background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/53148/box-image_8.jpg);
    }

    .image-9 {
      background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/53148/box-image_9.jpg);
    }

    .image-10 {
      background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/53148/box-image_10.jpg);
    }

    .image-11 {
      background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/53148/box-image_11.jpg);
    }
  </style>
</head>

<body>

  <div class="box">
    <div class="image-wrap">
      <div class="image image-0"></div>
    </div>
    <div class="border"></div>
    <div class="text">
      <span>Sugar plum</span>
    </div>
  </div>

  <div class="box">
    <div class="image-wrap">
      <div class="image image-1"></div>
    </div>
    <div class="border"></div>
    <div class="text">
      <span>apple pie</span>
    </div>
  </div>

  <div class="box">
    <div class="image-wrap">
      <div class="image image-2"></div>
    </div>
    <div class="border"></div>
    <div class="text">
      <span>gummy bears</span>
    </div>
  </div>

  <div class="box">
    <div class="image-wrap">
      <div class="image image-3"></div>
    </div>
    <div class="border"></div>
    <div class="text">
      <span>sesame snaps</span>
    </div>
  </div>

  <div class="box">
    <div class="image-wrap">
      <div class="image image-4"></div>
    </div>
    <div class="border"></div>
    <div class="text">
      <span>chupa chups</span>
    </div>
  </div>

  <div class="box">
    <div class="image-wrap">
      <div class="image image-5"></div>
    </div>
    <div class="border"></div>
    <div class="text">
      <span>chocolate cookie</span>
    </div>
  </div>

  <div class="box">
    <div class="image-wrap">
      <div class="image image-6"></div>
    </div>
    <div class="border"></div>
    <div class="text">
      <span>cheescake muffin</span>
    </div>
  </div>

  <div class="box">
    <div class="image-wrap">
      <div class="image image-7"></div>
    </div>
    <div class="border"></div>
    <div class="text">
      <span>sweet toffee</span>
    </div>
  </div>

  <div class="box">
    <div class="image-wrap">
      <div class="image image-8"></div>
    </div>
    <div class="border"></div>
    <div class="text">
      <span>carrot cake</span>
    </div>
  </div>

  <div class="box">
    <div class="image-wrap">
      <div class="image image-9"></div>
    </div>
    <div class="border"></div>
    <div class="text">
      <span>lollipop</span>
    </div>
  </div>

  <div class="box">
    <div class="image-wrap">
      <div class="image image-10"></div>
    </div>
    <div class="border"></div>
    <div class="text">
      <span>macaroon</span>
    </div>
  </div>

  <div class="box">
    <div class="image-wrap">
      <div class="image image-11"></div>
    </div>
    <div class="border"></div>
    <div class="text">
      <span>lemon tart</span>
    </div>
  </div>

  <script>
    // for storing mouse x / y position
    var mousePos = {
      x: -10,
      y: -10
    };

    // select the .box DOM elements
    var boxElements = document.getElementsByClassName('box');

    // create an array of objects to store the box elements and their image
    // positions
    var boxes = [];
    for (var i = 0; i < boxElements.length; i++) {
      boxes.push({
        el: boxElements[i],
        targetX: 0,
        targetY: 0,
        prevX: 0,
        prevY: 0,
        x: 0,
        y: 0,
        left: boxElements[i].offsetLeft,
        top: boxElements[i].offsetTop,
        size: boxElements[i].offsetWidth
      })
    }

    function mousemove(e) {
      // update mouse position
      mousePos.x = e.pageX;
      mousePos.y = e.pageY;
    }

    function updateBox(box) {
      // check if mouse is in box area
      if (mousePos.x > box.left && mousePos.x < (box.left + box.size) &&
        mousePos.y > box.top && mousePos.y < (box.top + box.size)) {
        // the mouse is in the space over the box - update the box image target position dependent on how far the mouse position is from the center of the box (box size/2)
        box.targetX = (box.size / 2 - (mousePos.x - box.left)) * 0.1;
        box.targetY = (box.size / 2 - (mousePos.y - box.top)) * 0.1;
      } else {
        // otherwise the box isn't being hovered, its target is 0
        box.targetX = 0;
        box.targetY = 0;
      }

      // update the image element position by lerping position to target
      // https://codepen.io/rachsmith/post/animation-tip-lerp
      box.x += (box.targetX - box.x) * 0.2;
      box.y += (box.targetY - box.y) * 0.2;

      // check that the values aren't really small already, to overcome javascripts poor handling of high precision math
      if (Math.abs(box.x) < .001) box.x = 0;
      if (Math.abs(box.y) < .001) box.y = 0;

      // only update CSS if the position has changed since last loop
      if (box.prevX !== box.x && box.prevY !== box.y) {
        // update css of image element
        box.el.children[0].children[0].style.transform = 'translate3d(' + box.x + 'px, ' + box.y + 'px, 0)';
      }

      // update prev values for next comparison
      box.prevX = box.x;
      box.prevY = box.y;
    }

    function loop() {
      // in the loop - updated each of the boxes
      for (var i = 0, l = boxes.length; i < l; i++) {
        updateBox(boxes[i]);
      }
      requestAnimationFrame(loop);
    }

    function resize() {
      // the box positions/sizes have updated on resize, so they need to be
      // reset
      for (var i = 0; i < boxes.length; i++) {
        boxes[i].left = boxes[i].el.offsetLeft;
        boxes[i].top = boxes[i].el.offsetTop;
        boxes[i].size = boxes[i].el.offsetWidth;
      }
    }

    // attach the mouse event listener to the document
    document.addEventListener('mousemove', mousemove);
    // listen for resize event, so box sizes can be updated
    window.addEventListener('resize', resize);
    // run the animation loop
    loop();


  </script>
</body>

</html>